"""
    Copyright (C) 2009  Andrew Stanton

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

"""

from inspect import ismethod, isclass, isfunction
from lxml import etree
import copy

class Parser(type):
    def __init__(cls, name, bases, dct):
        methodDict = {}
        callMethod = getattr(cls, '__call__')
        popLst = []
        for attr, value in dct.iteritems():
            if isfunction(value) and not attr.startswith('_'):
                methodDict[attr] = Parser.__methodWrapper__(value, value.__doc__.strip())
                popLst.append(attr)
            elif isinstance(value, (str, tuple)) and not attr.startswith('_'):
                methodDict[attr] = Parser.__xpathWrapper__(value)
                popLst.append(attr)
            elif isclass(value):
                methodDict[attr] = value()
                popLst.append(attr)
        for a in popLst:
            del dct[a]

        # Set the new call method
        if callMethod:
            setattr(cls, '__call__', Parser.__callWrapper__(callMethod, methodDict))
        super(Parser, cls).__init__(name, bases, dct)

    @staticmethod
    def __methodWrapper__(method, xpath):
        type = getattr(method, '__funcType__', None)
        if type:
            return type(method, xpath)
        def call(root):
            node = root.xpath(xpath)
            if node:
                if len(node) == 1:
                    return method(node[0])
                return [method(n) for n in node]
        return call
    
    @staticmethod
    def __callWrapper__(callMethod, methodDict):
        def call(self, root=None):
            arg = self.__doc__.strip()
            return callMethod(self, methodDict, arg, root)
        return call

    @staticmethod
    def __xpathWrapper__(value):
        # We just want to extract the text from the string.
        if isinstance(value, str):
            def xpathText(root):
                nodes = root.xpath(value)
                if nodes: 
                    # We have a string, return it
                    return ''.join(nodes)
                return None
            return xpathText
        # Must be a tuple: short hand for fire this class/func with the
        # results of this xpath.
        def xpathAttr(root):
            nodes = root.xpath(value[0])
            if isclass(value[1]):
                if len(nodes) == 1:
                    return value[1](nodes[0])()
                return [copy.deepcopy(value[1])(n)() for n in nodes]
            else:
                if len(nodes) ==1:
                    return value[1](nodes[0])
                return [value[1](n) for n in nodes]
        return xpathAttr

class BaseParser(type):
    def __init__(cls, name, bases, dct):
        setattr(cls, '__metaclass__', Parser)
        setattr(cls, '__class__', Parser)
        type.__init__(cls, name, bases, dct)

